﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PFXDemo.Library
{
    public class PFXAddition
    {
        private static List<List<int>> _list;
        private const int MaxListLength = 200000;
        
        /// <summary>
        /// Sets up a semi-random list of numbers and add them up as well
        /// </summary>
        public PFXAddition()
        {
            //Set up a list, 
            _list = new List<List<int>>();
            var random = new Random(Environment.TickCount);
            for (int i = 0; i < MaxListLength; i++)
            {
                int listLength = random.Next(50, 100);
                var temp = new List<int>();
                for (int j = 0; j < listLength; j++)
                {
                    int value = random.Next(0, 1000);
                    Total += value;
                    temp.Add(value);
                }
                _list.Add(temp);
            }
        }
        public long Total { get; set;}

        /// <summary>
        /// A nested inner and outer for loop
        /// </summary>
        public long SequentialAdditionFor()
        {
            long total=0;
            for (int i = 0; i < MaxListLength; i++)
            {
                for (int j = 0; j < _list[i].Count ; j++)
                {
                    total += _list[i][j];
                }
            }
            return (total);
        }

        /// <summary>
        /// A nested inner and outer foreach loop
        /// </summary>
        /// <returns>Total</returns>
        public long SequentialAdditionForEach()
        {
            long total = 0;
            foreach (List<int> list in _list)
            {
                foreach (int i in list)
                {
                    total += i;
                }
            }
            return (total);
        }

        /// <summary>
        /// A parallel for loop outer, seq inner
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionOuter()
        {
            long total = 0;

            Parallel.For(
                0, MaxListLength, i =>
                            {
                                int total1 = 0;
                                for (int j = 0; j < _list[i].Count; j++)
                                {
                                    total1 += _list[i][j];
                                }
                                Interlocked.Add(ref total, total1);
                            }
                );
            return (total);
        }

        public long ParallelAdditionOuterX()
        {

            Parallel.For(0, 100, delegate(int i)
                                     {
                                         Console.WriteLine(i);
                                     }
                );


            Parallel.For(0, 100, Console.WriteLine);

            return (0);
        }

        /// <summary>
        /// A parallel foreach loop outer, seq inner
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionForEachOuter()
        {
            long total = 0;
            Parallel.ForEach(_list, list =>
                                        {
                                            int total1 = 0;
                                            foreach (int i in list)
                                            {
                                                total1 += i;
                                                           
                                            }
                                            Interlocked.Add(ref total, total1);
                                        }
                );
 
            return (total);
        }

        public void DoStuff (object obj)
        {
            
        }
 


        /// <summary>
        /// A parallel for loop inner, seq outer
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionForInner()
        {
            long total = 0;

            for (int i = 0; i < MaxListLength; i++)
            {
                long total1 = 0;
                int i1 = i; //Prevent access to a modified closure
                Parallel.For(0, _list[i].Count, j => 
                                                    {

                                                        Interlocked.Add(ref total1,
                                                                        _list[i1][j]);
                                                    }
                    );
                total += total1;
            }
                


            return (total);
        }

        /// <summary>
        /// A parallel foreach loop inner, seq outer
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionForEachInner()
        {
            long total = 0;
            foreach (List<int> list in _list)
            {
                long total1 = 0;
                Parallel.ForEach(list, i =>
                                           {
                                               Interlocked.Add(ref total1, i);
                                           });
                Interlocked.Add(ref total, total1);
            }
                


            return (total);
        }

        /// <summary>
        /// Both for loops parallel 
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionForBoth()
        {
            long total = 0;

            Parallel.For<long>(0, MaxListLength, () => 0, (i, state) =>
                                                              {
                                                                  long total1 = 0;
                                                                  Parallel.For(0, _list[i].Count, delegate(int j)
                                                                                                      {
                                                                                                          Interlocked.Add(ref total1,
                                                                                                                          _list[i][j]);
                                                                                                      }
                                                                      );
                                                                  Interlocked.Add(ref total,total1);
                                                              }
                );


            return (total);
        }

        /// <summary>
        /// Both foreach loops parallel 
        /// </summary>
        /// <returns>Total</returns>
        public long ParallelAdditionForEachBoth()
        {
            long total = 0;
            Parallel.ForEach(_list, list =>
                                        {
                                            long total1 = 0;
                                            Parallel.ForEach<int>(list, delegate(int i)
                                                                            {
                                                                                Interlocked.Add(ref total1, i);
                                                                            });
                                            Interlocked.Add(ref total, total1);
                                        }
                );
                
                
            return (total);
        }
    }
}